#pragma once
// -------------------------------------------------
// ZED Kit
// -------------------------------------------------
//   File Name: bit.hpp
//      Author: Ziming Li
//     Created: 2023-12-04
// -------------------------------------------------
// Copyright (C) 2023 MingYang Software Technology.
// -------------------------------------------------

#ifndef ZED_BIT_HPP
#define ZED_BIT_HPP

#include <algorithm>
#include <bit>
#include <type_traits>

namespace zed {

template <typename T, std::endian E>
class endian_impl
{
public:
    endian_impl(void);

    T get(void) const;
    void set(T n);
private:
    T m_raw;
};

template <typename T>
using big_endian = endian_impl<T, std::endian::big>;

template <typename T>
using little_endian = endian_impl<T, std::endian::little>;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementations

namespace detail {

template <size_t N>
void swap_bytes(void* bytes)
{
    unsigned char* begin = reinterpret_cast<unsigned char*>(bytes);
    std::reverse(begin, begin + N);
}

} // namespace detail

template <typename T, std::endian E>
endian_impl<T, E>::endian_impl(void)
{
    static_assert(sizeof(*this) == sizeof(T));
    static_assert(std::is_integral_v<T> && sizeof(T) > 1);
}

template <typename T, std::endian E>
T endian_impl<T, E>::get(void) const
{
    if constexpr (std::endian::native == E)
    {
        return m_raw;
    }
    else
    {
        T ret = m_raw;
        detail::swap_bytes<sizeof(T)>(&ret);
        return ret;
    }
}

template <typename T, std::endian E>
void endian_impl<T, E>::set(T n)
{
    if constexpr (std::endian::native != E)
        detail::swap_bytes<sizeof(T)>(&n);
    m_raw = n;
}

} // namespace zed

#endif // ZED_BIT_HPP
